package MP.syntaxanalysis;
/*--------------------------------------------------
 *Ho va ten : HA LE HOAI TRUNG
 *MSSV : 50402810
 ---------------------------------------------------*/
import java.util.*;
import MP.CompilationException;
import MP.lexicalanalysis.Scanner;
import MP.lexicalanalysis.Token;
import MP.lexicalanalysis.ErrorReporter;

public class Parser {
    private Scanner scanner;
    private ErrorReporter reporter;

    private Token lookaheadToken; //look ahead 1 token

    public Parser(Scanner s, ErrorReporter r) {
        scanner = s;
        reporter = r;
        lookaheadToken = null;
    }

    public void init() {
	    if (lookaheadToken != null) {
		    throw new RuntimeException("Cannot run Parser.init");
	    }
	    lookaheadToken = scanner.nextToken();
    }
    public AST parse() throws CompilationException {
        
        init();
        Nonterminal p = (new Program());
		p.parse(this,null);
		if (lookaheadToken.Kind != Token.EOF) {

			throw new SyntaxException(getLookaheadToken());
		}
		return p.out;        
		
    }
    
    public Nonterminal getStartNonterminal() {
	    return new Program();
    }
    
    public void match(int tokenKind) throws CompilationException {
        if (lookaheadToken.Kind == tokenKind) {
            lookaheadToken = scanner.nextToken();
        } else {

            throw new SyntaxException(getLookaheadToken());
        }
    }

    public boolean check(int tokenKind) {
		return lookaheadToken.Kind == tokenKind;
    }
     
    public Token getLookaheadToken() {
	    return lookaheadToken;
    }
    
    public Scanner getScanner() {
	    return scanner;
    }
    
    public ErrorReporter getReporter() {
	    return reporter;
    }
}
	abstract class Nonterminal {
	    AST in,out;
	    ArrayList listId = new ArrayList();
	    int i = 0;//Dung lam count va flag
	    Token token; 
	    abstract Nonterminal parse(Parser o,Nonterminal n) throws CompilationException;
	}
    //<program> 			::= 	program <identifier> ; <variable declaration part> <procedure declaration part> <compound statement> .
    class Program extends Nonterminal { 
        Nonterminal parse(Parser o, Nonterminal n) throws CompilationException {
    		o.match(Token.PROGRAM);
    		o.match(Token.ID);
    		o.match(Token.SEMICOLON);
    		Nonterminal v = new VariableDeclarationPart();
			v.parse(o,this);
    		Nonterminal p = new ProcedureDeclarationPart();
			p.parse(o,this);
    		Nonterminal c = new CompoundStatement();
			c.parse(o,this);
			out = new ProgramAST((VarDeclPartAST) v.out,(ProDeclPartAST) p.out, (CompStatAST) c.out);
    		o.match(Token.DOT);
    		return this;
        }
    }
    
    //<variable declaration part> 	::= empty| Declconst VarDecl| Declid VarDecl	
    class VariableDeclarationPart extends Nonterminal { 
        Nonterminal parse(Parser o, Nonterminal n) throws CompilationException 
        {
        	if(listId.size() > 1)
        	{
        		VarDeclAST vd = new VarDeclAST((Token) listId.get(0) ,(TypeAST) in);
        		listId.remove(0);
        		Nonterminal vdp = new VariableDeclarationPart();
        		vdp.in = in;
        		vdp.listId = listId;
        		vdp.parse(o,this);
        		out = new VarDeclPartAST((OneDeclAST) vd , (VarDeclPartAST) vdp.out);
        		return this; 
        	}
        	//xu ly khi ma phan tu cuoi cung can them 1 lan quet de biet co khong phan tu tiep theo
        	else if(listId.size() == 1)
        	{
        		VarDeclAST vd = new VarDeclAST((Token) listId.get(0) ,(TypeAST) in);
        		listId.remove(0);
        		o.match(Token.SEMICOLON);//het phan khai bao bien
        		Nonterminal vdp = new VariableDeclarationPart();
        		vdp.parse(o,this);
        		out = new VarDeclPartAST((OneDeclAST) vd , (VarDeclPartAST) vdp.out);        		 
        		return this;
        	}
        	//---------di theo bien
        	else if(o.check(Token.CONST))
        	{
        		Nonterminal dconst = new DelcConst();
        		dconst.parse(o,this);
        		Nonterminal vP = new VariableDeclarationPart();
        		vP.parse(o,this);
        		out = new VarDeclPartAST((OneDeclAST) dconst.out , (VarDeclPartAST) vP.out);
        		return this;        	
        	}
        	//dung
        	else if(o.check(Token.VAR))
        	{
        		Nonterminal did = new DeclId();//khong phai 1 vardecl;
        		did.parse(o,this);        		
        		out = did.out;
        		return this;  
        	}
        	else if(o.check(Token.PROCEDURE) || o.check(Token.BEGIN) || o.check(Token.WHILE) ||
        		o.check(Token.FUNCTION) || o.check(Token.END) || o.check(Token.ID) || o.check(Token.FOR)|| 
        		o.check(Token.IF) || o.check(Token.REPEAT) || o.check(Token.BREAK) ||
        		o.check(Token.CONTINUE) || o.check(Token.BEGIN))      	
			{				
				out = new EmptyVarDeclPartAST();
			}		
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
        }
    }
    
    //Delconst := const id = literals 
    class DelcConst extends Nonterminal
    {
    	Nonterminal parse(Parser o , Nonterminal n) throws CompilationException 
    	{    	        		
        	o.match(Token.CONST);
        	Token temp = o.getLookaheadToken();//Dung de lay token ID de truyen cho Literal
        	o.match(Token.ID);
        	o.match(Token.EQOP);        		
        	Nonterminal odc = new Literals();
        	odc.parse(o , this);
        	o.match(Token.SEMICOLON); 
        	out = new ConstDeclAST(temp,(LiteralAST) odc.out);        	
        	return this;
    	}
    }
    
    // decl_id := var id_list : type_kind 
    class DeclId extends Nonterminal
    {
    	Nonterminal parse(Parser o , Nonterminal n)throws CompilationException
    	{
    		o.match(Token.VAR);
    		IdList idl = new IdList();
    		idl.parse(o,this);
    		Token tke = (Token) idl.listId.get(0);
    		idl.listId.remove(0);
    		o.match(Token.COLON);
    		
    		TypeKind tk = new TypeKind();
    		tk.parse(o,this);
    		
    		if(idl.listId.size() > 0)
    		{    		
    			Nonterminal vdp = new VariableDeclarationPart();
    			vdp.in = tk.out;
    			vdp.listId = idl.listId;
    			vdp.parse(o,this);
    			VarDeclAST vd = new VarDeclAST( tke, (TypeAST) tk.out);
    			out = new VarDeclPartAST((OneDeclAST) vd , (VarDeclPartAST) vdp.out);
    		}
    		else
    		{
    			VarDeclAST vd = new VarDeclAST( tke, (TypeAST) tk.out);
    			o.match(Token.SEMICOLON);
    			Nonterminal vdp = new VariableDeclarationPart();
    			vdp.parse(o,this);
    			out = new VarDeclPartAST((OneDeclAST) vd , (VarDeclPartAST) vdp.out); 
    		}    		   		    		
    		return this;    			  		
    	}
    }    
  
    //IdList := id idList1
    class IdList extends Nonterminal
    {    	
    	Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
    	{
    		Token token = o.getLookaheadToken();
    		listId.add(i,token);
    		i++;
    		o.match(Token.ID);
    		IdList1 idl = new IdList1();
    		idl.parse(o,this);
    		if(!(idl.listId.isEmpty()))
    		{
    			listId.addAll(i,idl.listId);
    		}    				
    		return this;
    	}
    }
    
    //IdList1 := ,id idList1 || Empty
    class IdList1 extends Nonterminal
    {    	    	
    	Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
    	{
    		if(o.check(Token.COMMA))
    		{
    			o.match(Token.COMMA);
    			Token token = o.getLookaheadToken();
    			o.match(Token.ID);
    			listId.add(i,token);
    			i++;
    			IdList1 idl1 = new IdList1();
    			idl1.parse(o,this);
    			if(!(idl1.listId.isEmpty()))
    			{
    				listId.addAll(i,idl1.listId);
    			}
    			return this;
    		}
    		else if(o.check(Token.COLON))
    		{
    			return this;
    		}
    		else
	    		throw new SyntaxException(o.getLookaheadToken());
    	}
    }
    
    //<TypeKind> ::=	Type | array [intliteral .. intliteral] of type 
	class TypeKind extends Nonterminal 
	{
		Nonterminal parse(Parser o , Nonterminal n) throws CompilationException
		{			
			if(o.check(Token.INTEGER) || o.check(Token.REAL) || 
				o.check(Token.BOOLEAN))
			{
				Type type = new Type();
				type.parse(o,this);
				out = type.out;
				return this;
			}				
			else if(o.check(Token.ARRAY))
			{
				o.match(Token.ARRAY);
				o.match(Token.LEFTSQURBRK);
				Token intl1 = o.getLookaheadToken();
				o.match(Token.INTLITERAL);
				o.match(Token.RANGE);
				Token intl2 = o.getLookaheadToken();
				o.match(Token.INTLITERAL);
				o.match(Token.RIGHTSQURBRK);
				o.match(Token.OF);
				Type type = new Type();
				type.parse(o,this);
				out = new ArrayTypeAST((PrimTypeAST) type.out ,intl1 ,intl2);
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//<Type> := Intergr | real | boolean 
	class Type extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{
			if(o.check(Token.INTEGER))
			{
				o.match(Token.INTEGER);
				out = new IntTypeAST();				
			}
			else if(o.check(Token.REAL))
			{
				o.match(Token.REAL);
				out = new RealTypeAST();
			}
			else if(o.check(Token.BOOLEAN))
			{
				o.match(Token.BOOLEAN);
				out = new BoolTypeAST();
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//<Literals ::= intliteral | realliteral | booleanliteral 
	class Literals extends Nonterminal
	{		
		Nonterminal parse(Parser o , Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();
			if(o.check(Token.INTLITERAL))
			{
				o.match(Token.INTLITERAL);
				out = new IntLiteralAST(t);
			}
			else if(o.check(Token.REALLITERAL))
			{
				o.match(Token.REALLITERAL);
				out = new RealLiteralAST(t);
			}
			else if(o.check(Token.BOOLEANLITERAL))
			{
				o.match(Token.BOOLEANLITERAL);
				out = new BoolLiteralAST(t);
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}

    //<procedure declaration part> 	::= 		
	class ProcedureDeclarationPart extends Nonterminal { 
	    Nonterminal parse(Parser o, Nonterminal n) throws CompilationException {
	    	if(o.check(Token.PROCEDURE))
	    	{
	    		o.match(Token.PROCEDURE);
	    		Token namePro = (Token) o.getLookaheadToken();
	    		o.match(Token.ID);
	    		o.match(Token.LEFTPAR);
	    		Para p = new Para();
	    		p.parse(o,this);
	    		o.match(Token.RIGHTPAR);
	    		o.match(Token.SEMICOLON);
	    		VariableDeclarationPart vdp = new VariableDeclarationPart();
	    		vdp.parse(o,this);
	    		ProcedureDeclarationPart pdp = new ProcedureDeclarationPart();
	    		pdp.parse(o,this);
	    		CompoundStatement cps = new CompoundStatement();
	    		cps.parse(o,this);
	    		o.match(Token.SEMICOLON);
	    		//Tao 1 Procedure
	    		ProcDeclAST opd = new ProcDeclAST(namePro, (ParaListAST) p.out,(VarDeclPartAST) vdp.out,
	    		 (ProDeclPartAST) pdp.out,(CompStatAST) cps.out);
	    		
	    		ProcedureDeclarationPart lpdp = new ProcedureDeclarationPart();
	    		lpdp.parse(o,this);
	    		out = new ProDeclPartAST(opd ,(ProDeclPartAST) lpdp.out);
	    	}
	    	else if(o.check(Token.FUNCTION))
	    	{
	    		o.match(Token.FUNCTION);
	    		Token namePro = (Token) o.getLookaheadToken();
	    		o.match(Token.ID);
	    		o.match(Token.LEFTPAR);
	    		Para p = new Para();
	    		p.parse(o,this);
	    		o.match(Token.RIGHTPAR);
	    		o.match(Token.COLON);
	    		Type t = new Type();
	    		t.parse(o,this);
	    		o.match(Token.SEMICOLON);
	    		VariableDeclarationPart vdp = new VariableDeclarationPart();
	    		vdp.parse(o,this);
	    		ProcedureDeclarationPart pdp = new ProcedureDeclarationPart();
	    		pdp.parse(o,this);
	    		CompoundStatement cps = new CompoundStatement();
	    		cps.parse(o,this);
	    		o.match(Token.SEMICOLON);
	    		//Tao 1 Procedure
	    		FuncDeclAST opd = new FuncDeclAST(namePro, (ParaListAST)p.out, (TypeAST) t.out,
	    		(VarDeclPartAST) vdp.out, (ProDeclPartAST)pdp.out, (CompStatAST)cps.out);
	    		
	    		ProcedureDeclarationPart lpdp = new ProcedureDeclarationPart();
	    		lpdp.parse(o,this);
	    		out = new ProDeclPartAST(opd ,(ProDeclPartAST) lpdp.out);
	    	}
	    	else if(o.check(Token.BEGIN))
	    	{
	    		out = new EmptyProDeclPartAST();
	    	}
	    	else
	    		throw new SyntaxException(o.getLookaheadToken());
		return this;
	    }
	}
	
	//<Para> := empty | idlist : type ; para1
	class Para extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n)throws CompilationException
		{
			if(o.check(Token.ID))
			{
				IdList idl = new IdList();
				idl.parse(o,this);
				listId = idl.listId; 
				o.match(Token.COLON);
				TypeKind t = new TypeKind();
				t.parse(o,this);
				in = t.out;
				Token namePa = (Token) listId.get(0);
				listId.remove(0);
				VarDeclAST vd = new VarDeclAST(namePa ,(TypeAST) in);
				ParaList p1 = new ParaList();
				p1.listId = listId;
				p1.in = in;					
				p1.parse(o,this);
				out = new ParaListAST(vd , (ParaListAST)p1.out);				
			}			
			else if(o.check(Token.RIGHTPAR) || (listId.size() == 0))
			{
				out = new EmptyParaListAST();
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//<ParaList> := ; idlist : typekind Paralist | empty
	class ParaList extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			if(listId.size() > 1)
			{
				Token namePa = (Token) listId.get(0);
				listId.remove(0);
				VarDeclAST vd = new VarDeclAST(namePa, (TypeAST) in);
				ParaList pl = new ParaList();
				pl.listId = listId;
				pl.in = in;
				pl.parse(o,this);
				out = new ParaListAST (vd, (ParaListAST) pl.out);
			}
			else if(listId.size() == 1)
			{
				Token namePa = (Token) listId.get(0);
				listId.remove(0);
				VarDeclAST vd = new VarDeclAST(namePa, (TypeAST) in);
				ParaList pl = new ParaList();
				pl.parse(o,this);
				out = new ParaListAST (vd, (ParaListAST) pl.out);
			}		
			else if(o.check(Token.SEMICOLON))
			{
				o.match(Token.SEMICOLON);
				IdList idl = new IdList();
				idl.parse(o,this);				
				o.match(Token.COLON);
				TypeKind t = new TypeKind();
				t.parse(o,this);				
				ParaList pl = new ParaList();
				pl.in = t.out;
				pl.listId = idl.listId;
				pl.parse(o,this);
				out = pl.out;
			}
			else if(o.check(Token.RIGHTPAR))
			{
				out = new EmptyParaListAST();
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());			
			return this;
		}
	}
	
    //<compound statement> 		::= 	begin <variable declaration part> <statementList> end
	class CompoundStatement extends Nonterminal { 	
	    Nonterminal parse(Parser o, Nonterminal n) throws CompilationException 
	    {	    
    		o.match(Token.BEGIN);    		  		
    		Nonterminal vdp = (new VariableDeclarationPart());
			vdp.parse(o,this);
			StateList stl = new StateList();
			stl.parse(o,this);
			o.match(Token.END);
			out = new CompStatAST((VarDeclPartAST) vdp.out, (StmtListAST) stl.out);
    		return this;
	    }
	}
	
	//<StateList> := Statement StateList
	class StateList extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{			
			if(o.check(Token.ID) || o.check(Token.WHILE) || o.check(Token.FOR) ||
				(o.check(Token.IF)) || o.check(Token.REPEAT) || o.check(Token.BREAK) ||
				(o.check(Token.CONTINUE)) || o.check(Token.BEGIN ))
			{			
				Statement stm = new Statement();
				stm.parse(o,this);
				StateList stl = new StateList();
				stl.parse(o,this);
				out = new StmtListAST((OneStmtAST) stm.out, (StmtListAST) stl.out);
			}
			else if(o.check(Token.END) || o.check(Token.UNTIL))
			{
				out = new EmptyStmtListAST();
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
    //<statement> 			::= <assignment_call statement>	;|| WhileState || ForState
    //	|| IfState || RepeateState || break ; || continue ; || CompoundStatement					
	class Statement extends Nonterminal { 
	    Nonterminal parse(Parser o, Nonterminal n) throws CompilationException {
			Nonterminal st;
	    	if (o.check(Token.ID)){	    		
	    		st = new AssignmentOrCallStatement();
	    		st.parse(o,this);
	    		o.match(Token.SEMICOLON);
				out = st.out;
	    	}
	    	else if(o.check(Token.WHILE))
	    	{
	    		st = new WhileState();
	    		st.parse(o,this);
	    		out = st.out;
	    	}
	    	else if(o.check(Token.FOR))
	    	{
	    		st = new ForState();
	    		st.parse(o,this);
	    		out = st.out;
	    	}
	    	else if(o.check(Token.IF))
	    	{
	    		st = new IfState();
	    		st.parse(o,this);
	    		out = st.out;
	    	}
	    	else if(o.check(Token.REPEAT))
	    	{
	    		st = new RepeateState();
	    		st.parse(o,this);
	    		out = st.out;
	    	}
	    	else if(o.check(Token.BREAK))
	    	{
	    		out = new BreakStmtAST();
	    		o.match(Token.BREAK);
	    		o.match(Token.SEMICOLON);
	    	}
	    	else if(o.check(Token.CONTINUE))
	    	{
	    		out = new ContStmtAST();
	    		o.match(Token.CONTINUE);
	    		o.match(Token.SEMICOLON);
	    	}
	    	else if(o.check(Token.BEGIN))
	    	{
	    		st = new CompoundStatement();
	    		st.parse(o,this);
	    		out = st.out;
	    	}
	    	else
	    		throw new SyntaxException(o.getLookaheadToken());			
	    	return this;
	    }
	}
	
	//WhileState := while expr do Statement
	class WhileState extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			o.match(Token.WHILE);
			Nonterminal expr = new Expr();
			expr.parse(o,this);
			o.match(Token.DO);
			Nonterminal stmt = new Statement();
			stmt.parse(o,this);
			out = new WhileStmtAST((ExprAST) expr.out, (OneStmtAST) stmt.out);
			return this;
		}
	}
	
	//ForState := for id := expr UpDown expr do Statement
	class ForState extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			boolean bool;
			o.match(Token.FOR);
			Token tk = o.getLookaheadToken();
			o.match(Token.ID);
			o.match(Token.ASSIGNOP);
			Nonterminal exp1 = new Expr();
			exp1.parse(o,this);
			Nonterminal ud = new UpDown();
			ud.parse(o,this);
			if(ud.i == 1)
			{
				bool = true;
			}
			else
			{
				bool = false;
			}
			Nonterminal exp2 = new Expr();
			exp2.parse(o,this);
			o.match(Token.DO);
			Nonterminal stmt = new Statement();
			stmt.parse(o,this);
			out = new ForStmtAST(tk, (ExprAST) exp1.out, (ExprAST) exp2.out, bool, (OneStmtAST) stmt.out);
			return this;
		}
	}
	
	//UpDown := to | downto
	class UpDown extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			if(o.check(Token.TO))
			{
				o.match(Token.TO);
				i = 1;
			}
			else if(o.check(Token.DOWNTO))
			{
				o.match(Token.DOWNTO);
				i = 0;
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//IfState := if expr then Statement ElseState
	class IfState extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			o.match(Token.IF);
			Nonterminal expr = new Expr();
			expr.parse(o,this);
			o.match(Token.THEN);
			Nonterminal stmt = new Statement();
			stmt.parse(o,this);
			Nonterminal elseS = new ElseState();
			elseS.parse(o,this);
			if(elseS.i == 1)
			{
				out = new IfThenElseStmtAST((ExprAST) expr.out, (OneStmtAST) stmt.out, (OneStmtAST) elseS.out);
			}
			else
			{
				out = new IfThenStmtAST((ExprAST) expr.out, (OneStmtAST) stmt.out);
			}
			return this;
		}
	}
	
	//ElseState := else Statement | empty
	class ElseState extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			if(o.check(Token.ELSE))
			{
				i = 1;
				o.match(Token.ELSE);
				Nonterminal stmt = new Statement();
				stmt.parse(o,this);
				out = stmt.out;
			}
			else if(o.check(Token.END) || o.check(Token.ID) || o.check(Token.WHILE) ||
				o.check(Token.FOR) || o.check(Token.IF) ||o.check(Token.REPEAT) || 
				o.check(Token.BREAK) || o.check(Token.CONTINUE) || o.check(Token.BEGIN) ||
				o.check(Token.UNTIL))
			{
				i = 0 ;	
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//RepeateState := repeate Statement StateList until Expr ;
	class RepeateState extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			o.match(Token.REPEAT);
			Nonterminal stmt = new Statement();
			stmt.parse(o,this);
			Nonterminal stml = new StateList();
			stml.parse(o,this);
			o.match(Token.UNTIL);
			Nonterminal exp = new Expr();
			exp.parse(o,this);
			o.match(Token.SEMICOLON);
			out = new RepeatStmtAST(new StmtListAST((OneStmtAST) stmt.out, (StmtListAST) stml.out), (ExprAST) exp.out);
			return this;
		}
	}
		
    //<assignment_call statement> 		::= 	<identifier> := INTLITERAL ;
	class AssignmentOrCallStatement extends Nonterminal { 
	    Nonterminal parse(Parser o, Nonterminal n) throws CompilationException {
	    	Token id = o.getLookaheadToken();
			o.match(Token.ID);
			//chi la bien 
	    	if (o.check(Token.ASSIGNOP)) 
	    	{				
				o.match(Token.ASSIGNOP);
				Expr expr = new Expr();
				expr.parse(o,this);
				out = new AssiStmtAST(new LvalueAST(id,new EmptyExprListAST()), (ExprAST) expr.out);	    		
	    	}
	    	//day mang 
	    	else if(o.check(Token.LEFTSQURBRK))
	    	{
	    		o.match(Token.LEFTSQURBRK);
	    		Nonterminal expr = new Expr();
	    		expr.parse(o,this);
	    		o.match(Token.RIGHTSQURBRK);
	    		o.match(Token.ASSIGNOP);
	    		Nonterminal exprl = new Expr();
	    		exprl.parse(o,this);
	    		LvalueAST lv = new LvalueAST(id ,new ExprListAST((ExprAST) expr.out, new EmptyExprListAST()));
	    		out = new AssiStmtAST( lv, (ExprAST) exprl.out);
	    	}
	    	//ham , thu tuc
	    	else if(o.check(Token.LEFTPAR))
	    	{
	    		o.match(Token.LEFTPAR);
	    		Nonterminal exp = new ExprPara();
	    		exp.parse(o,this);
	    		o.match(Token.RIGHTPAR);
	    		out = new CallStmtAST(id,(ExprListAST) exp.out);
	    	}
	    	else
	    		throw new SyntaxException(o.getLookaheadToken());
    		return this;
	    }
	}
	
	//Expr := ExprAdd ExprList
	class Expr extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			Nonterminal expradd = new ExprAdd();
			expradd.parse(o,this);
			Nonterminal exprl = new ExprList();
			exprl.in = expradd.out;
			exprl.parse(o,this);
			out = exprl.out;
			return this;
		}
	}
	
	//ExprList := OpRelative ExprAdd ExprList | empty
	class ExprList extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();
			if(o.check(Token.RELOP) || o.check(Token.EQOP))
			{			
			Nonterminal op = new OpRelative();
			op.parse(o,this);
			Nonterminal opradd = new ExprAdd();
			opradd.parse(o,this);
			Nonterminal oprl = new ExprList();
			oprl.in = new BinExprAST((ExprAST) in , op.token ,(ExprAST) opradd.out);
			oprl.parse(o,this);
			out = oprl.out;
			}
			else if(o.check(Token.RIGHTSQURBRK) || o.check(Token.SEMICOLON) || o.check(Token.DO) || o.check(Token.COMMA)||
				(o.check(Token.TO)) || o.check(Token.DOWNTO) || o.check(Token.RIGHTPAR) || o.check(Token.THEN))
				{
					out = in;
				}
			else
	    		throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//OpRelative := = | > | < | >= | <= | <>
	class OpRelative extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();
			if(o.check(Token.EQOP))
			{
				token = t;
				o.match(Token.EQOP);
			}
			else if(o.check(Token.RELOP))
			{
				token = t;
				o.match(Token.RELOP);
			}
			else 
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	} 
		
	//ExprAdd := ExprMul ExprAddList 
	class ExprAdd extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			Nonterminal exprm = new ExprMul();
			exprm.parse(o,this);
			Nonterminal expral = new ExprAddList();
			expral.in = exprm.out;
			expral.parse(o,this);
			out = expral.out;
			return this;
		}
	}
	
	//ExprAddList := OpAdd ExprMul ExprAddList || empty
	class ExprAddList extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{			
			if(o.check(Token.ADDOP) || o.check(Token.OR))
			{
				Nonterminal oa = new OpAdd();
				oa.parse(o,this);
				Nonterminal exm = new ExprMul();
				exm.parse(o,this);
				Nonterminal expral = new ExprAddList();//
				expral.in = new BinExprAST((ExprAST) in, oa.token, (ExprAST) exm.out);
				expral.parse(o,this);
				out = expral.out;
			}
			else if(o.check(Token.EQOP) || o.check(Token.RELOP) || o.check(Token.RIGHTSQURBRK) || o.check(Token.SEMICOLON) || o.check(Token.COMMA)||
				(o.check(Token.TO)) || o.check(Token.DOWNTO) || o.check(Token.DO) || o.check(Token.RIGHTPAR) || o.check(Token.THEN))
				{
					out = in;
				}
			else 
				throw new SyntaxException(o.getLookaheadToken());				
			return this;
		}
	}
	
	//OpAdd := + | - | or
	class OpAdd extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();
			if(o.check(Token.ADDOP ))
			{
				token = t;
				o.match(Token.ADDOP);
			}
			else if(o.check(Token.OR))
			{
				token = t;
				o.match(Token.OR);
			}
			else 
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//ExprMul := ExprUna ExprMulList
	class ExprMul extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{
			Nonterminal eu = new ExprUna();
			eu.parse(o,this);
			Nonterminal em = new ExprMulList();
			em.in = eu.out;
			em.parse(o,this);
			out = em.out;
			return this;
		}
	}
	
	//ExprMulList := OpMul ExprUna ExprMulList | empty
	class ExprMulList extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{			
			if(o.check(Token.MULOP) || o.check(Token.DIV) || o.check(Token.MOD) || o.check(Token.AND))
			{
				Nonterminal om = new OpMul();
				om.parse(o,this);
				Nonterminal eu = new ExprUna();
				eu.parse(o,this);
				Nonterminal eml = new ExprMulList();
				eml.in = new BinExprAST((ExprAST) in, om.token , (ExprAST)eu.out);
				eml.parse(o,this);
				out = eml.out;
			}
			else if(o.check(Token.ADDOP) || o.check(Token.OR) || o.check(Token.EQOP) || o.check(Token.RELOP )||
				(o.check(Token.RIGHTSQURBRK)) ||o.check(Token.SEMICOLON) || o.check(Token.COMMA)||
				(o.check(Token.TO)) || o.check(Token.DOWNTO )|| o.check(Token.DO) || o.check(Token.RIGHTPAR ) || o.check(Token.THEN))
				{
					out = in;
				}
			else 
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//OpMul := * | / | div | mod | and
	class OpMul extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();
			if(o.check(Token.MULOP))
			{
				token = t;
				o.match(Token.MULOP);
			}
			else if(o.check(Token.DIV))
			{
				token = t;
				o.match(Token.DIV);
			}
			else if(o.check(Token.MOD))
			{
				token = t;
				o.match(Token.MOD);					
			}
			else if(o.check(Token.AND))
			{
				token = t;
				o.match(Token.AND);
			}
			else
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//ExprUna := OpUna ExprUna | factor
	class ExprUna extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{			
			if(o.check(Token.NOT) || o.check(Token.ADDOP))
			{
				Nonterminal ou = new OpUna();
				ou.parse(o,this);
				Nonterminal eu = new ExprUna();
				eu.parse(o,this);
				out = new UnaryExprAST(ou.token, (ExprAST) eu.out);
			}
			else if(o.check(Token.INTLITERAL) || o.check(Token.REALLITERAL) || o.check(Token.STRINGLITERAL) ||
				(o.check(Token.BOOLEANLITERAL)) || o.check(Token.ID) || o.check(Token.LEFTPAR))
			{
				Nonterminal f = new Factor();
				f.parse(o,this);
				out = f.out;	
			}
			else
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//OpUna := not | + | -
	class OpUna extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();			
			if(o.check(Token.ADDOP ))
			{
				token = t;
				o.match(Token.ADDOP);
			}
			else if(o.check(Token.NOT))
			{
				token = t;
				o.match(Token.NOT);
			}
			else
				throw new SyntaxException(o.getLookaheadToken()); 
			return this;
		}
	}
	
	//Factor := id IdKind | (Expr) | Literals | stringliteral
	class Factor extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();
			if(o.check(Token.ID))
			{				
				Nonterminal idk = new IdKind();				
				idk.parse(o,this);
				out = idk.out;
			}
			else if(o.check(Token.LEFTPAR))
			{
				o.match(Token.LEFTPAR);
				Nonterminal ex = new Expr();
				ex.parse(o,this);
				out = ex.out;
				o.match(Token.RIGHTPAR);
			}
			else if(o.check(Token.INTLITERAL) || o.check(Token.REALLITERAL) || 
				(o.check(Token.BOOLEANLITERAL)) || o.check(Token.STRINGLITERAL))
			{
				if(o.check(Token.INTLITERAL))
				{
					o.match(Token.INTLITERAL);
					out = new IntLiteralAST(t);
				}
				else if(o.check(Token.REALLITERAL))
				{
					o.match(Token.REALLITERAL);
					out = new RealLiteralAST(t);
				}
				else if(o.check(Token.BOOLEANLITERAL))
				{
					o.match(Token.BOOLEANLITERAL);
					out = new BoolLiteralAST(t);
				}
				else if(o.check(Token.STRINGLITERAL))
				{
					o.match(Token.STRINGLITERAL);
					out = new StringLiteralAST(t);
				}
			}
			else
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//IdKind := [Expr] | (ExprPara) | empty
	class IdKind extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{
			Token t = o.getLookaheadToken();
			o.match(Token.ID);			
			if(o.check(Token.LEFTSQURBRK))
			{
				o.match(Token.LEFTSQURBRK);
				Nonterminal ex = new Expr();
				ex.parse(o,this);
				o.match(Token.RIGHTSQURBRK);
				out = new EleExprAST(t, (ExprAST) ex.out);
			}
			else if(o.check(Token.LEFTPAR))
			{
				o.match(Token.LEFTPAR);
				Nonterminal exp = new ExprPara();
				exp.parse(o,this);
				o.match(Token.RIGHTPAR);
				out = new CallExprAST(t, (ExprListAST) exp.out);
			}
			else if(o.check(Token.MULOP) || o.check(Token.DIV) || o.check(Token.MOD) ||o.check(Token.AND )||
				(o.check(Token.ADDOP))|| o.check(Token.OR) || o.check(Token.EQOP) || o.check(Token.RELOP) ||
				(o.check(Token.RIGHTSQURBRK)) || o.check(Token.SEMICOLON) || o.check(Token.COMMA)|| o.check(Token.THEN)||
				(o.check(Token.TO)) || o.check(Token.DOWNTO )|| o.check(Token.DO )|| o.check(Token.RIGHTPAR))
			{
				out = new VarExprAST(t);
			}
			else
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}
	
	//ExprPara := ExprParaList | empty
	class ExprPara extends Nonterminal
	{
		Nonterminal parse(Parser o,Nonterminal n) throws CompilationException
		{		
			if(o.check(Token.NOT) || o.check(Token.ADDOP) || o.check(Token.INTLITERAL) || 
				(o.check(Token.REALLITERAL)) || o.check(Token.STRINGLITERAL) ||
				(o.check(Token.BOOLEANLITERAL)) || o.check(Token.ID) || o.check(Token.LEFTPAR))
			{
				Nonterminal ex = new Expr();
				ex.parse(o,this);
				Nonterminal expl = new ExprParaList();
				expl.parse(o,this);
				out = new ExprListAST((ExprAST) ex.out, (ExprListAST) expl.out);
			}
			else if(o.check(Token.RIGHTPAR))
			{				
				out = new EmptyExprListAST();
			}
			else
	    		throw new SyntaxException(o.getLookaheadToken());		
			return this;
		}
	}
	
	//ExprParaList := Expr ExprParaListl
	class ExprParaList extends Nonterminal
	{
		Nonterminal parse(Parser o, Nonterminal n) throws CompilationException
		{
			if(o.check(Token.COMMA))
			{
				o.match(Token.COMMA);
				Nonterminal ex = new Expr();
				ex.parse(o,this);
				Nonterminal exprl = new ExprParaList();
				exprl.parse(o,this);
				out = new ExprListAST((ExprAST) ex.out,(ExprListAST) exprl.out);				
			}
			else if(o.check(Token.RIGHTPAR))
			{
				out = new EmptyExprListAST();
			}
			else
				throw new SyntaxException(o.getLookaheadToken());
			return this;
		}
	}	
	
	
	
